/*
 ------------------------------------------------------------------------------
 Copyright (C) 2006-2007 Team Blur.

 This file is part of the Quantum Engine source code.

 The Quantum Engine source code is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or (at your
 option) any later version.

 The Quantum Engine source code is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 more details.

 You should have received a copy of the GNU General Public License along with
 the Quantum Engine source code; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 ------------------------------------------------------------------------------
*/


#include "s_local.h"


/*
 ==================
 S_PlaceListener
 ==================
*/
void S_PlaceListener (int listenerId, int time, const odVec3 &position, const odVec3 &velocity, const odMat3 &axis, bool underwater, int area){

	snd.listener.listenerId = listenerId;
	snd.listener.time = time;

	snd.listener.position.Set(position.y, position.z, -position.x);
	snd.listener.velocity.Set(velocity.y, velocity.z, -velocity.x);

	snd.listener.orientation[0] = axis[0][1];
	snd.listener.orientation[1] = -axis[0][2];
	snd.listener.orientation[2] = -axis[0][0];
	snd.listener.orientation[3] = axis[2][1];
	snd.listener.orientation[4] = -axis[2][2];
	snd.listener.orientation[5] = -axis[2][0];

	snd.listener.underwater = underwater;
	snd.listener.area = area;

	// Edit the current reverb if the editor is active
	S_EditCurrentReverb();
}

/*
 ==================
 S_ShakeAmplitudeForListener
 ==================
*/
float S_ShakeAmplitudeForListener (void){

	channel_t		*channel;
	soundShader_t	*soundShader;
	float			distance, occludedDistance;
	float			attenuation, amplitude;
	float			shakeAmplitude = 0.0f;
	int				offset;
	int				i;

	if (s_skipShakes->integerValue)
		return 0.0f;

	// Query all channels
	for (i = 0, channel = snd.channels; i < snd.numChannels; i++, channel++){
		if (!channel->sound)
			continue;

		if (channel->local)
			continue;

		if (!channel->soundShader->shakes)
			continue;		// No shakes

		soundShader = channel->soundShader;

		// Sample the sound
		qalGetSourcei(channel->sourceId, AL_SAMPLE_OFFSET, &offset);

		offset = M_ClampLong(offset, 0, channel->sound->samples - 1);

		amplitude = ((ushort *)channel->sound->data)[offset] * (1.0f/65535);

		// Update the channel if needed
		if (channel->emitter){
			channel->position = channel->emitter->position;
			channel->velocity = channel->emitter->velocity;

			channel->underwater = channel->emitter->underwater;
			channel->area = channel->emitter->area;
		}

		// Calculate amplitude
		if (s_skipAttenuation->integerValue || (soundShader->flags & SSF_GLOBAL)){
			// Account for volume
			amplitude *= soundShader->volume;

			// Scale
			amplitude *= soundShader->shakes;
		}
		else {
			// Find the distance to the listener
			distance = snd.listener.position.DistanceFast(channel->position);
			distance = M_ClampFloat(distance, soundShader->minDistance, soundShader->maxDistance);

			// Account for occlusion
			if (!s_skipOcclusion->integerValue && !(soundShader->flags & SSF_NOOCCLUSION)){
				if (S_SoundIsOccluded(channel->area, distance, soundShader->maxDistance, &occludedDistance))
					distance = occludedDistance;
			}

			// Compute distance attenuation
			if (soundShader->maxDistance > soundShader->minDistance && soundShader->rolloffFactor){
				attenuation = 1.0f - (soundShader->rolloffFactor * (distance - soundShader->minDistance) / (soundShader->maxDistance - soundShader->minDistance));

				amplitude *= M_ClampFloat(attenuation, 0.0f, 1.0f);
			}

			// Account for underwater
			if (snd.listener.underwater || channel->underwater)
				amplitude *= 0.5f;

			// Account for volume
			amplitude *= soundShader->volume;

			// Scale
			amplitude *= soundShader->shakes;
		}

		// Add to shake amplitude
		shakeAmplitude += amplitude;
	}

	return shakeAmplitude;
}

/*
 ==================
 S_UpdateListener
 ==================
*/
void S_UpdateListener (void){

	reverb_t	*reverb;

	if (s_logFile->integerValue)
		QAL_LogPrintf("----- S_UpdateListener -----\n");

	// Update listener parameters
	qalListenerfv(AL_POSITION, snd.listener.position.ToFloatPtr());
	qalListenerfv(AL_VELOCITY, snd.listener.velocity.ToFloatPtr());

	qalListenerfv(AL_ORIENTATION, snd.listener.orientation);

	if (snd.active || snd.efx.reverbEditing)
		qalListenerf(AL_GAIN, s_masterVolume->floatValue);
	else
		qalListenerf(AL_GAIN, 0.0f);

	// Update reverb
	if (!snd.efx.reverb)
		return;

	if (snd.listener.area == -1)
		reverb = &snd.efx.reverbGeneric;
	else {
		if (snd.listener.underwater)
			reverb = &snd.efx.reverbUnderwater;
		else
			reverb = &snd.efx.reverbList[snd.listener.area];
	}

	if (snd.listener.reverb == reverb)
		return;		// Reverb hasn't changed
	snd.listener.reverb = reverb;

	qalEffectf(snd.efx.effectId, AL_EAXREVERB_DENSITY, reverb->density);
	qalEffectf(snd.efx.effectId, AL_EAXREVERB_DIFFUSION, reverb->diffusion);
	qalEffectf(snd.efx.effectId, AL_EAXREVERB_GAIN, reverb->gain);
	qalEffectf(snd.efx.effectId, AL_EAXREVERB_GAINHF, reverb->gainHF);
	qalEffectf(snd.efx.effectId, AL_EAXREVERB_GAINLF, reverb->gainLF);
	qalEffectf(snd.efx.effectId, AL_EAXREVERB_DECAY_TIME, reverb->decayTime);
	qalEffectf(snd.efx.effectId, AL_EAXREVERB_DECAY_HFRATIO, reverb->decayHFRatio);
	qalEffectf(snd.efx.effectId, AL_EAXREVERB_DECAY_LFRATIO, reverb->decayLFRatio);
	qalEffecti(snd.efx.effectId, AL_EAXREVERB_DECAY_HFLIMIT, reverb->decayHFLimit);
	qalEffectf(snd.efx.effectId, AL_EAXREVERB_REFLECTIONS_GAIN, reverb->reflectionsGain);
	qalEffectf(snd.efx.effectId, AL_EAXREVERB_REFLECTIONS_DELAY, reverb->reflectionsDelay);
	qalEffectfv(snd.efx.effectId, AL_EAXREVERB_REFLECTIONS_PAN, reverb->reflectionsPan.ToFloatPtr());
	qalEffectf(snd.efx.effectId, AL_EAXREVERB_LATE_REVERB_GAIN, reverb->lateReverbGain);
	qalEffectf(snd.efx.effectId, AL_EAXREVERB_LATE_REVERB_DELAY, reverb->lateReverbDelay);
	qalEffectfv(snd.efx.effectId, AL_EAXREVERB_LATE_REVERB_PAN, reverb->lateReverbPan.ToFloatPtr());
	qalEffectf(snd.efx.effectId, AL_EAXREVERB_ECHO_TIME, reverb->echoTime);
	qalEffectf(snd.efx.effectId, AL_EAXREVERB_ECHO_DEPTH, reverb->echoDepth);
	qalEffectf(snd.efx.effectId, AL_EAXREVERB_MODULATION_TIME, reverb->modulationTime);
	qalEffectf(snd.efx.effectId, AL_EAXREVERB_MODULATION_DEPTH, reverb->modulationDepth);
	qalEffectf(snd.efx.effectId, AL_EAXREVERB_HFREFERENCE, reverb->hfReference);
	qalEffectf(snd.efx.effectId, AL_EAXREVERB_LFREFERENCE, reverb->lfReference);
	qalEffectf(snd.efx.effectId, AL_EAXREVERB_AIR_ABSORPTION_GAINHF, reverb->airAbsorptionGainHF);
	qalEffectf(snd.efx.effectId, AL_EAXREVERB_ROOM_ROLLOFF_FACTOR, reverb->roomRolloffFactor);

	qalAuxiliaryEffectSloti(snd.efx.effectSlotId, AL_EFFECTSLOT_EFFECT, snd.efx.effectId);
}
